﻿using Quartz;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ZYing.QPanel
{
    public class ExecutionHistoryPlugin : ISchedulerPlugin, IJobListener
    {
        public string Name { get; private set; }
        public Type StoreType { get; set; }
        private IScheduler _scheduler;

        public Task Initialize(string pluginName, IScheduler scheduler, CancellationToken cancellationToken = default)
        {
            this.Name = pluginName;
            this._scheduler = scheduler;
            scheduler.ListenerManager.AddJobListener(this);
            return Task.CompletedTask;
        }

        public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            if (context.Scheduler.Context.TryGetExecutionHistoryStore(out var store))
            {
                var entry = await store.Find(context.JobDetail.Key, context.FireInstanceId);
                if (entry != null)
                {
                    entry.Vetoed = true;
                    await store.Save(entry);
                }
            }
        }

        public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
        {
            if (context.Scheduler.Context.TryGetExecutionHistoryStore(out var store))
            {
                var entry = new ExecutionHistoryEntry()
                {
                    FireInstanceId = context.FireInstanceId,
                    SchedulerInstanceId = context.Scheduler.SchedulerInstanceId,
                    SchedulerName = context.Scheduler.SchedulerName,
                    ActualFireTimeUtc = context.FireTimeUtc.UtcDateTime,
                    ScheduledFireTimeUtc = context.ScheduledFireTimeUtc?.UtcDateTime,
                    Recovering = context.Recovering,
                    Job = context.JobDetail.Key,
                    Trigger = context.Trigger.Key,
                    Class = context.JobDetail.JobType.GetFullClass()
                };
                await store.Save(entry);
            }
            context.Put("StateReporter", new Action<string>(msg => JobHelper.FireStatus(context, msg)));
        }

        public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
        {
            if (context.Scheduler.Context.TryGetExecutionHistoryStore(out var store))
            {
                var entry = await store.Find(context.JobDetail.Key, context.FireInstanceId);
                if (entry != null)
                {
                    entry.FinishedTimeUtc = DateTime.UtcNow;
                    entry.ExceptionMessage = jobException?.GetBaseException()?.Message;
                    await store.Save(entry);
                }
                if (jobException == null)
                {
                    await store.IncrementTotalJobsExecuted();
                }
                else
                {
                    await store.IncrementTotalJobsFailed();
                    JobHelper.FireStatus(context, $"发生未处理的异常:{jobException}");
                }
            }
        }

        public Task Shutdown(CancellationToken cancellationToken = default)
        {
            return Task.CompletedTask;
        }

        public async Task Start(CancellationToken cancellationToken = default)
        {
            if (_scheduler == null) return;
            if (!_scheduler.Context.TryGetExecutionHistoryStore(out var store))
            {
                if (this.StoreType == null) throw new System.Configuration.ConfigurationErrorsException($"{nameof(this.StoreType)} is not set.");
                store = (IExecutionHistoryStore)Activator.CreateInstance(this.StoreType);
                _scheduler.Context.SetExecutionHistoryStore(store);
            }
            store.SchedulerName = _scheduler.SchedulerName;
            await store.Purge();
        }
    }
}
